/*
 *  Copyright 2011 Alexey Andreev.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package org.xthl.core;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.nop.core.ManagedBean;
import org.nop.sql.DataManager;
import org.nop.sql.DataResult;
import org.nop.sql.QueryBuilder;
import org.nop.util.Injected;
import org.xthl.core.api.Block;
import org.xthl.core.api.BlockLogic;
import org.xthl.core.api.CustomBlock;
import org.xthl.core.api.RestrictedAccessException;
import org.xthl.core.api.SpecialBlock;
import org.xthl.core.api.UpdateCustomBlockCommand;
import org.xthl.core.data.BlockSource;

/**
 *
 * @author Alexey Andreev
 */
@ManagedBean(iface = BlockLogic.class)
public class BlockLogicImpl implements BlockLogic {
    private DataManager dataManager;
    private QueryBuilder qb;
    
    @Injected
    public BlockLogicImpl(DataManager dataManager) {
        this.dataManager = dataManager;
        this.qb = dataManager.getQueryBuilder();
    }

    @Override
    public int createCustomBlock(String title, String content) {
        BlockSource block = qb.get(BlockSource.class);
        int id = dataManager.nextInt(BlockSource.ID_SEQUENCE);
        int ver = dataManager.nextInt(BlockSource.VERSION_SEQUENCE);
        DataResult result = dataManager.exec(qb.with(block)
                .fetch(qb.max(block.position())));
        result.next();
        int pos = result.getInt(1) + 1;
        dataManager.exec(qb.insertInto(block)
                .field(block.id(), id)
                .field(block.position(), pos)
                .field(block.type(), BlockSource.CUSTOM_TYPE)
                .field(block.enabled(), false)
                .field(block.title(), title)
                .field(block.content(), content)
                .field(block.version(), ver));
        return id;
    }
    
    private void checkBlockExists(int blockId, String type) {
        BlockSource block = qb.get(BlockSource.class);
        DataResult result = dataManager.exec(qb.with(block)
                .filter(block.id().eq(blockId))
                .fetch(block.type()));
        if (!result.next()) {
            throw new IllegalArgumentException("Block #" + blockId + " does not exist");
        }
        if (type != null && !type.equals(result.getString(1))) {
            throw new IllegalArgumentException("Block #" + blockId + " is not custom");
        }
    }

    @Override
    public void updateCustomBlock(int blockId, UpdateCustomBlockCommand command)
            throws RestrictedAccessException {
        checkBlockExists(blockId, BlockSource.CUSTOM_TYPE);
        BlockSource block = qb.get(BlockSource.class);
        int ver = dataManager.nextInt(BlockSource.VERSION_SEQUENCE);
        dataManager.exec(qb.with(block)
                .filter(block.id().eq(blockId))
                .set(block.title(), command.getTitle())
                .set(block.content(), command.getContent())
                .set(block.version(), ver));
    }

    @Override
    public void deleteCustomBlock(int blockId) {
        checkBlockExists(blockId, BlockSource.CUSTOM_TYPE);
        BlockSource block = qb.get(BlockSource.class);
        dataManager.exec(qb.with(block).filter(block.id().eq(blockId)).delete());
    }

    @Override
    public String getCustomBlockContent(int blockId) {
        BlockSource block = qb.get(BlockSource.class);
        DataResult result = dataManager.exec(qb.with(block)
                .filter(block.id().eq(blockId))
                .fetch(block.type())
                .fetch(block.content()));
        if (!result.next()) {
            throw new IllegalArgumentException("Block #" + blockId + " does not exist");
        }
        if (!result.getString(1).equals(BlockSource.CUSTOM_TYPE)) {
            throw new IllegalArgumentException("Block #" + blockId + " is not custom");
        }
        return result.getString(2);
    }

    @Override
    public void enableBlocks(Set<Integer> blockIds, boolean enable) {
        BlockSource block = qb.get(BlockSource.class);
        dataManager.exec(qb.with(block)
                .filter(block.id().inNumbers(blockIds))
                .set(block.enabled(), enable ? qb.getTrue() : qb.getFalse()));
    }
    
    private Class<?> getRendererType(String name) {
        try {
            return Class.forName(name);
        } catch (ClassNotFoundException ex) {
            return null;
        }
    }
    
    private Block getBlock(DataResult result) {
        String type = result.getString(2);
        Block common;
        if (type.equals(BlockSource.CUSTOM_TYPE)) {
            CustomBlock block = new CustomBlock();
            common = block;
            block.setTitle(result.getString(5));
            block.setVersion(result.getInt(6));
        } else if (type.equals(BlockSource.SPECIAL_TYPE)) {
            SpecialBlock block = new SpecialBlock();
            common = block;
            block.setRendererType(getRendererType(result.getString(5)));
            if (block.getRendererType() == null) {
                return null;
            }
        } else {
            throw new IllegalStateException("Unknown block type: " + type);
        }
        common.setId(result.getInt(1));
        common.setPosition(result.getInt(3));
        common.setEnabled(result.getBoolean(4));
        return common;
    }

    @Override
    public List<Block> getBlocks() {
        BlockSource block = qb.get(BlockSource.class);
        DataResult result = dataManager.exec(qb.with(block)
                .sortAsc(block.position())
                .fetch(block.id(), block.type(), block.position(), block.enabled(),
                        block.title(), block.version()));
        List<Block> dtos = new ArrayList<Block>();
        while (result.next()) {
            dtos.add(getBlock(result));
        }
        return dtos;
    }

    @Override
    public Block getBlock(int blockId) {
        BlockSource block = qb.get(BlockSource.class);
        DataResult result = dataManager.exec(qb.with(block)
                .filter(block.id().eq(blockId))
                .fetch(block.id(), block.type(), block.position(), block.enabled(),
                        block.title(), block.version()));
        return result.next() ? getBlock(result) : null;
    }

    @Override
    public List<Block> getEnabledBlocks() {
        BlockSource block = qb.get(BlockSource.class);
        DataResult result = dataManager.exec(qb.with(block)
                .filter(block.enabled().isTrue())
                .sortAsc(block.position())
                .fetch(block.id(), block.type(), block.position(), block.enabled(),
                        block.title(), block.version()));
        List<Block> dtos = new ArrayList<Block>();
        while (result.next()) {
            dtos.add(getBlock(result));
        }
        return dtos;
    }

    @Override
    public void moveBlock(int blockId, int targetPosition) {
        BlockSource block = qb.get(BlockSource.class);
        DataResult result = dataManager.exec(qb.with(block)
                .filter(block.id().eq(blockId))
                .fetch(block.position()));
        result.next();
        int currentPosition = result.getInt(1);
        if (currentPosition > targetPosition) {
            dataManager.exec(qb.with(block)
                    .filter(block.position().ge(targetPosition))
                    .filter(block.position().le(currentPosition))
                    .set(block.position(), qb.cond()
                        .when(block.position().ne(currentPosition),
                        block.position().add(qb.wrap(1)))
                        .otherwise(qb.wrap(targetPosition))));
        } else if (currentPosition < targetPosition) {
            dataManager.exec(qb.with(block)
                    .filter(block.position().ge(currentPosition))
                    .filter(block.position().le(targetPosition))
                    .set(block.position(), qb.cond()
                        .when(block.position().ne(currentPosition),
                        block.position().sub(qb.wrap(1)))
                        .otherwise(qb.wrap(targetPosition))));
        }
    }
}
